{***************************************************************************}
{                                                                           }
{           Spring Framework for Delphi                                     }
{                                                                           }
{           Copyright (C) 2009-2011 DevJET                                  }
{                                                                           }
{           http://www.spring4d.org                                         }
{                                                                           }
{***************************************************************************}
{                                                                           }
{  Licensed under the Apache License, Version 2.0 (the "License");          }
{  you may not use this file except in compliance with the License.         }
{  You may obtain a copy of the License at                                  }
{                                                                           }
{      http://www.apache.org/licenses/LICENSE-2.0                           }
{                                                                           }
{  Unless required by applicable law or agreed to in writing, software      }
{  distributed under the License is distributed on an "AS IS" BASIS,        }
{  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
{  See the License for the specific language governing permissions and      }
{  limitations under the License.                                           }
{                                                                           }
{***************************************************************************}

unit Spring.Serialization.Formatter;

{$I Spring.inc}

interface

uses
  Classes,
  SysUtils,
  Rtti,
  Generics.Collections,
  Spring,
  Spring.Collections;

type

  IContentNode = interface;
  IContentDocument = interface;

  IFormatter = interface
  ['{84CEC258-3BA9-473E-A926-C9139B2B3534}']
    procedure ContentToTree(const stream: TStream; const document: IContentDocument);
    procedure TreeToContent(const stream: TStream; const document: IContentDocument);

    function CreateDocument: IContentDocument;
  end;

  IContentDocument = interface
    ['{3C3FEB4C-1C15-41A7-98E8-D37B5B6CC8BD}']
    {$REGION 'Property Accessors'}
      function GetRoot: IContentNode;
    {$ENDREGION}

    procedure LoadFromStream(const stream: TStream);
    procedure SaveToStream(const stream: TStream);

    property Root: IContentNode read GetRoot;
  end;

  IContentNode = interface
    ['{3031D31D-0675-4076-9F12-55C53EA718FB}']
    {$REGION 'Property Accessors'}
      function GetChildren: IEnumerable<IContentNode>;
      //function GetAttributes: IDictionary<string, string>;
      function GetName: string;
      function GetValue: string;
      function GetDeclaredType: string;
      procedure SetName(const value: string);
      procedure SetValue(const value: string);
      procedure SetDeclaredType(const value: string);
    {$ENDREGION}

    function AddChild: IContentNode;
    function FindChild(const name: string): IContentNode;
    procedure Clear;

    property Children: IEnumerable<IContentNode> read GetChildren;
    property Name: string read GetName write SetName;
    property Value: string read GetValue write SetValue;
    property DeclaredType: string read GetDeclaredType write SetDeclaredType;
  end;

  TFormatterBase = class abstract(TInterfacedObject, IFormatter, IInterface)
  protected
    procedure ContentToTree(const content: TStream; const document: IContentDocument); virtual; abstract;
    procedure TreeToContent(const content: TStream; const document: IContentDocument); virtual; abstract;
  public
    function CreateDocument: IContentDocument;
  end;

  TContentDocument = class(TInterfacedObject, IContentDocument, IInterface)
  private
    fRoot: IContentNode;
    fMedium: IFormatter;

    {$REGION 'Property Accessors'}
      function GetRoot: IContentNode;
    {$ENDREGION}
  public
    constructor Create(const medium: IFormatter);
    procedure LoadFromStream(const stream: TStream);
    procedure SaveToStream(const stream: TStream);

    property Root: IContentNode read GetRoot;
  end;

  TContentNode = class abstract(TInterfacedObject, IContentNode, IInterface)
  private
    fName: string;
    fValue: string;
    fDeclaredType: string;
    fChildren: IList<IContentNode>;

    {$REGION 'Property Accessors'}
      function GetChildren: IEnumerable<IContentNode>;
      function GetName: string;
      function GetValue: string;
      function GetDeclaredType: string;
      procedure SetName(const value: string);
      procedure SetValue(const value: string);
      procedure SetDeclaredType(const value: string);
    {$ENDREGION}
    function GetChildrenList: IList<IContentNode>;

    property ChildrenList: IList<IContentNode> read GetChildrenList;
  public
    property Name: string read GetName write SetName;
    property Value: string read GetValue write SetValue;
    function AddChild: IContentNode;
    property Children: IEnumerable<IContentNode> read GetChildren;

    function FindChild(const name: string): IContentNode;

    procedure Clear;
  end;

implementation

uses
  Spring.Serialization,
  Spring.Serialization.Base,
  Spring.Serialization.ResourceStrings;

{$REGION 'TXmlBackendNode'}

function TContentNode.AddChild: IContentNode;
begin
  Result := TContentNode.Create;
  ChildrenList.Add(Result);
end;

procedure TContentNode.Clear;
begin
  fName := '';
  fValue := '';
  fChildren := nil;
end;

function TContentNode.FindChild(const name: string): IContentNode;
var
  node: IContentNode;
begin
  Result := nil;
  for node in Children do
  begin
    if node.Name = name then
    begin
      Result := node;
      break;
    end;
  end;
end;

function TContentNode.GetChildren: IEnumerable<IContentNode>;
begin
  Result := ChildrenList;
end;

function TContentNode.GetChildrenList: IList<IContentNode>;
begin
  if not Assigned(fChildren) then
    fChildren := TCollections.CreateList<IContentNode>;

  Result := fChildren;
end;

function TContentNode.GetDeclaredType: string;
begin
  Result := fDeclaredType;
end;

function TContentNode.GetName: string;
begin
  Result := fName;
end;

function TContentNode.GetValue: string;
begin
  Result := fValue;
end;

procedure TContentNode.SetDeclaredType(const value: string);
begin
  fDeclaredType := value;
end;

procedure TContentNode.SetName(const value: string);
begin
  fName := value;
end;

procedure TContentNode.SetValue(const value: string);
begin
  fValue := value;
end;

{$ENDREGION}


{ TXmlBackendBase }

function TFormatterBase.CreateDocument: IContentDocument;
begin
  Result := TContentDocument.Create(self);
end;

{$REGION 'TXmlBackendDocument'}

constructor TContentDocument.Create(const medium: IFormatter);
begin
  inherited Create;
  fMedium := medium;
end;

function TContentDocument.GetRoot: IContentNode;
begin
  if not Assigned(fRoot) then
    fRoot := TContentNode.Create;

  Result := fRoot;
end;

procedure TContentDocument.LoadFromStream(const stream: TStream);
begin
  fMedium.ContentToTree(stream, Self);
end;

procedure TContentDocument.SaveToStream(const stream: TStream);
begin
  fMedium.TreeToContent(stream, Self);
end;

{$ENDREGION}

end.
